package com.googlecode.kipler.container.graph;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.googlecode.kipler.common.Copyable;

/**
 * This is actually a graph implementation.
 * 
 * @author İnanç Seylan
 */
public class RelationalModel<V extends RelationalValue<V, K>, R, K> implements
		Copyable<RelationalModel<V, R, K>> {

	private Map<K, RelationalNode<V, R, K>> nodeMap = new HashMap<K, RelationalNode<V, R, K>>();

	private List<RelationalNode<V, R, K>> nodeList = new ArrayList<RelationalNode<V, R, K>>();

	public RelationalModel() {
	}

	public RelationalModel<V, R, K> copy() {
		RelationalModel<V, R, K> result = new RelationalModel<V, R, K>();
		for (RelationalNode<V, R, K> node : nodeList) {
			result.add(node.copy());
		}
		for (RelationalNode<V, R, K> node : nodeMap.values()) {
			for (R rel : node.edgesOutgoing()) {
				for (RelationalNode<V, R, K> toNode : node.getOutgoing(rel)) {
					result.getNode(node.getValue().getKey()).addOutgoing(rel,
							result.getNode(toNode.getValue().getKey()));
				}
			}
			for (R rel : node.edgesIncoming()) {
				for (RelationalNode<V, R, K> fromNode : node.getIncoming(rel)) {
					result.getNode(node.getValue().getKey()).addIncoming(rel,
							result.getNode(fromNode.getValue().getKey()));
				}
			}
		}
		return result;
	}

	public RelationalNode<V, R, K> getNode(K key) {
		return nodeMap.get(key);
	}

	public RelationalNode<V, R, K> getNode(int i) {
		return nodeList.get(i);
	}

	public int getIndex(RelationalNode<V, R, K> node) {
		return nodeList.indexOf(node);
	}

	public Collection<RelationalNode<V, R, K>> getNodes() {
		return nodeList;
	}

	public int size() {
		return nodeMap.size();
	}

	public void add(RelationalNode<V, R, K> node) {
		K key = node.getValue().getKey();
		if (!nodeMap.containsKey(key)) {
			nodeMap.put(key, node);
			nodeList.add(node);
		}
	}

	public void remove(RelationalNode<V, R, K> node) {
		for (RelationalNode<V, R, K> owner : node.getIncoming()) {
			owner.removeOutgoing(node);
		}
		for (RelationalNode<V, R, K> filler : node.getOutgoing()) {
			filler.removeIncoming(node);
		}
		nodeMap.remove(node.getValue().getKey());
		nodeList.remove(node);
	}

	public boolean contains(K key) {
		return nodeMap.containsKey(key);
	}

	@Override
	public boolean equals(Object obj) {
		boolean result = false;
		if (obj != null && (obj.getClass().equals(this.getClass()))) {
			RelationalModel<?, ?, ?> other = (RelationalModel<?, ?, ?>) obj;
			result = nodeMap.equals(other.nodeMap);
		}
		return result;
	}

	@Override
	public String toString() {
		StringBuffer buffer = new StringBuffer();
		for (RelationalNode<V, R, K> node : nodeList) {
			buffer.append(node);
		}
		return buffer.toString();
	}

}
